其他
教你如何在Unity项目中消除JIT对卡顿的影响?
大家知道,C#是目前Unity使用最广泛的脚本语言,而C#首先会被翻译成CIL(Common Intermediate Language,通用中间语言,因为是微软开发,也称为MSIL,或简称IL)。而CIL可以在任何支持CLI(Common Language Infrastructure,通用语言基础结构)的环境中运行,如下图所示。感兴趣的童鞋,可以用反编译工具打开一个未加密的C#的dll看一下IL的真面目,或者看一下这篇博文,解释得很清楚http://www.cnblogs.com/murongxiaopifu/p/4211964.html。
而Unity采用了Mono这种CLI作为其编译、运行C#的处理器。此时终于轮到我们的主角JIT – Just In Time登场了。JIT是Mono的一种编译机制,相对于Mono的另一种编译机制AOT-Ahead Of Time来说,是一种更加灵活的处理方式。AOT是在编译期,就将所有的IL直接编译成机器可以识别的机器码,运行时直接执行机器码即可;而JIT,则是在运行时才将IL编译成机器码,这么做的好处,是可以实现代码的热更新,但是同时带来的,则是在首次运行时产生的编译消耗。
有了这个猜想之后,我们做了一个简单的测试,在游戏一开始运行的时候,将整个Assembly-CSharp.dll里面的所有函数全部找出,并且尝试访问一下这些函数,目的是提早触发JIT。
再次测试之后,性能热点消失了。不过随后就发现,预先JIT的时间消耗很大,即使在高端机上(我们在三星NOTE3上测试),也需要消耗8秒左右。因为每次启动游戏,都需要消耗8秒,这个对用户体验来说是个很大的伤害。
既然不能再使用这种量大面广的地图炮,我们只能尝试精确打击。要精确打击,就需要搜集到在性能热点发生时进行JIT的函数,然后只对这些函数所在的类(Type)进行预先JIT。由于游戏的逻辑复杂,而且引入各种组件之后(BT和AGE),调用栈极其深,想靠手动搜集这些函数似乎不太可能;进一步说,以后还会遇到其他的需要预先JIT的地方,手动搜集费时费力,也不具备可重用性。
此时我们又想到了Mono,因为JIT是在Mono中进行的,那么我们是否可以从Mono中尝试获取JIT相关的信息呢?
打开Mono的源代码搜索关键字“jit”,果然找到了一些相关的函数,经过筛选,锁定了一个函数:
从函数的注释中也可以看出,这里是JIT的必经之路,并且也可以通过参数MonoMethod获取到我们想要知道的函数信息。说干就干,在函数中添加了数据采集的代码,果然可以获取到JIT的信息了。
但是此时获取到的仍旧是全部的JIT信息,还不符合我们精确打击的要求。原理上说,我们需要在性能热点发生时,搜集 JIT的信息,很自然的,我们需要添加一个标志位来判定是否采集。同时在C#中,我们调用接口来开关这个标志位,就可以实现精确打击的目标了。
近期热文
经验分享丨项目实践项目孵化丨渠道发行做有梦想的游戏人
-GAME AND DREAM-